/*
 * Copyright (c) 2011 Sapienza University of Rome.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the
 *   distribution.
 * - Neither the name of the Sapienza University of Rome nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL SAPIENZA
 * UNIVERSITY OF ROME OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * This component is responsible for logging
 * @author Ugo Colesanti <colesanti@dis.uniroma1.it>
 * @version 1.01 (September 29, 2011)
 *
 */
#warning "************* FLASH LOGGING - ENABLED *************"
module FlashLogC{
	provides{
		interface FlashLog ;
	}
	uses {
		interface Leds ;
		interface LogRead ;
		interface LogWrite ;

		interface Timer<TMilli> as FlushInterleavingTimer ;

		interface AMSend as Send ;
		interface Receive ;

		interface PacketAcknowledgements ;
	  }
}
implementation{

	app_data_t flash_read_buffer ;

	message_t app_msg_buf ;
	app_data_t app_data_buf ;

	bool send_busy = FALSE ;
	bool log_busy = FALSE ;

	/**
	 * Once the beginning of the Flash has been found, starts reading logs and send it via radio packets.
	 */
	event void LogRead.seekDone(error_t error){
			if(error == SUCCESS) call LogRead.read(&flash_read_buffer, sizeof(app_data_t)) ;
	}

	event void FlushInterleavingTimer.fired(){
		if(call LogRead.read(&flash_read_buffer, sizeof(app_data_t)) == SUCCESS) call Leds.led0Toggle() ;
		else call Leds.led1On() ;
	}

	/**
	 * Until a valid log has been read, continue to send packets via AMSend interface.
	 * Otherwise do nothing.
	 */
	event void LogRead.readDone(void* buf, storage_len_t len, error_t err) {

		app_data_t* app_data_buf_ptr ;
		app_data_t* app_data ;
		uint8_t datareadings ;
		if(err == SUCCESS && len == sizeof(app_data_t)){
			app_data_buf_ptr = (app_data_t*) buf ;
			app_data = (app_data_t *) call Send.getPayload(&app_msg_buf, sizeof(app_data_t));
			if(app_data != NULL){
				// Copy the data to the radio buffer
				app_data->seqno = app_data_buf_ptr->seqno ;
				app_data->voltage = app_data_buf_ptr->voltage ;
				app_data->timestamp = app_data_buf_ptr->timestamp ;
				for(datareadings = 0; datareadings < SENSOR_COUNT; datareadings++){
					app_data->sensor[datareadings].data = app_data_buf_ptr->sensor[datareadings].data ;
					app_data->sensor[datareadings].temp = app_data_buf_ptr->sensor[datareadings].temp ;
				}

				// Enable link-layer acknowledgement and send the packet
				call PacketAcknowledgements.requestAck(&app_msg_buf) ;
				if (call Send.send(99,&app_msg_buf, sizeof(app_data_t)) == SUCCESS){
					send_busy = TRUE;
				}
			}
		}
	}

	event void Send.sendDone(message_t* msg, error_t err){
		send_busy = FALSE ;
		if(err == SUCCESS){
			atomic{
				call Leds.led2Toggle() ;
				if(! call PacketAcknowledgements.wasAcked(&app_msg_buf)){
					// If the packet has not been acked, resend it
					if (call Send.send(99,&app_msg_buf, sizeof(app_data_t)) == SUCCESS){
						send_busy = TRUE;
						return ;
					}
				}
				else{
					// Wait a short time and read the next log
					call FlushInterleavingTimer.startOneShot(128) ;
				}
			}
		}
	}

	/**
	 * Upon the reception of a FlashLog message of type FLUSH, the FlashLog component
	 * flushes all the flash memory via radio packets.
	 */
	event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
			if(sizeof(flashLogMsg_t) == len){
				flashLogMsg_t* flashLogRx = (flashLogMsg_t*) payload ;

				if(flashLogRx->type == FLUSH){
					call LogRead.seek(SEEK_BEGINNING) ;
				}
			}
			return msg ;
	}

	event void LogWrite.syncDone(error_t error){}
	event void LogWrite.eraseDone(error_t error){
	  call Leds.led2Toggle() ;
	}

	event void LogWrite.appendDone(void* buf, storage_len_t len,
			bool recordsLost, error_t err) {

		atomic{
			log_busy = FALSE ;
			call Leds.led1Toggle() ;
			signal FlashLog.logDone(buf,err) ;
		}
	}

	command error_t FlashLog.log(void* buf, storage_len_t len){
		return call LogWrite.append(buf,len) ;
	}

}
